/**
 * Subject 接口声明了 RealSubject 和 Proxy 都需要实现的公共操作。
 * 只要客户端使用这个接口与 RealSubject 进行交互，
 * 就可以将其传递给代理而不是真实的主题。
 */
interface Subject {
    request(): void; // 请求方法
}

/**
 * RealSubject 包含了一些核心业务逻辑，通常可以执行一些有用的工作，
 * 但可能会很慢或敏感 - 例如，校正输入数据。代理可以解决这些问题，而无需更改 RealSubject 的代码。
 */
// @ts-ignore
class RealSubject implements Subject {
    public request(): void {
        console.log('RealSubject: Handling request.'); // 处理请求的具体逻辑
    }
}

/**
 * 代理与 RealSubject 具有相同的接口。
 */
// @ts-ignore
class CustomProxy implements Subject {
    private realSubject: RealSubject;

    /**
     * 代理维护对 RealSubject 类对象的引用。它可以是延迟加载的，也可以由客户端传递给代理。
     */
    constructor(realSubject: RealSubject) {
        this.realSubject = realSubject;
    }

    /**
     * 代理模式最常见的应用包括延迟加载、缓存、控制访问、日志记录等。
     * 代理可以执行其中一种操作，然后根据结果将执行传递给关联的 RealSubject 对象的相同方法。
     */
    public request(): void {
        if (this.checkAccess()) { // 检查访问权限
            this.realSubject.request(); // 调用 RealSubject 的请求方法
            this.logAccess(); // 记录访问日志
        }
    }

    private checkAccess(): boolean {
        // 一些真实的检查应该在这里执行。
        console.log('Proxy: Checking access prior to firing a real request.'); // 检查访问权限
        return true; // 模拟始终通过
    }

    private logAccess(): void {
        console.log('Proxy: Logging the time of request.'); // 记录访问时间
    }
}

/**
 * 客户端代码应该使用 Subject 接口来与所有对象（主题和代理）一起工作，
 * 以支持真实主题和代理。然而，实际上，客户端主要直接与真实主题进行交互。
 * 在这种情况下，为了更容易地实现模式，你可以从真实主题的类扩展你的代理。
 */
// @ts-ignore
function clientCode(subject: Subject) {
    // ...
    subject.request(); // 客户端执行请求
    // ...
}

console.log('Client: Executing the client code with a real subject:');
const realSubject = new RealSubject(); // 创建真实主题对象
// @ts-ignore
clientCode(realSubject); // 使用真实主题对象执行客户端代码

console.log('代理使用场景:');

console.log('Client: Executing the same client code with a proxy:');
const proxy = new CustomProxy(realSubject); // 创建代理对象
// @ts-ignore
clientCode(proxy); // 使用代理对象执行相同的客户端代码
